<!-- $Id: TestAsyncUnitTest.html 2 2008-10-30 18:29:31Z gk-google@ninebynine.org $ -->
<!-- To run this test, just load it into a browser -->

<!--
    Test asynchronous unit test facilities.
    Initially based on MochiKit's test_MochiKit-Async.html

    To run tests in isolation, just load this file into a browser.
-->

<html>
  <head>
    <link rel="stylesheet" type="text/css" href="../../static/mochikit/tests/SimpleTest/test.css">
    <script type="text/javascript">MochiKit = {__export__: false};</script>
    <script type="text/javascript" src="../../static/javascript/MochiKit.js"></script>
    <script type="text/javascript" src="../../static/javascript/Namespace.js"></script>
    <script type="text/javascript" src="../../static/SimpleTest/SimpleTest.js"></script>
    <script type="text/javascript" src="../../static/javascript/AsyncUnitTest.js"></script>
    <script type="text/javascript" src="javascript/SimpleTestCases.js"></script>
  </head>
<body>

<pre id="test">
<script type="text/javascript">

/* Get MochiKit base functions into our global namespace */
compare            = MochiKit.Base.compare;
operator           = MochiKit.Base.operator;
bind               = MochiKit.Base.bind;
map                = MochiKit.Base.map;
arrayEqual         = MochiKit.Base.arrayEqual;

/* Get MochiKit async functions into our global namespace */
Deferred           = MochiKit.Async.Deferred;
DeferredList       = MochiKit.Async.DeferredList;
DeferredLock       = MochiKit.Async.DeferredLock;
succeed            = MochiKit.Async.succeed;
fail               = MochiKit.Async.fail;
wait               = MochiKit.Async.wait;
callLater          = MochiKit.Async.callLater;
evalJSONRequest    = MochiKit.Async.evalJSONRequest;
gatherResults      = MochiKit.Async.gatherResults;
maybeDeferred      = MochiKit.Async.maybeDeferred;
GenericError       = MochiKit.Async.GenericError;
CancelledError     = MochiKit.Async.CancelledError;
AlreadyCalledError = MochiKit.Async.AlreadyCalledError;

try {

    // Helper functions - all from test_MochiKit-Async.html

    var increment = function (res) {
        return res + 1;
    }

    var throwStuff = function (res) {
        throw new GenericError(res);
    }

    var catchStuff = function (res) {
        return res.message;
    }

    var returnError = function (res) {
        return new GenericError(res);
    }

    var anythingOkCallback = function (msg) {
        return function (res) {
            ok(true, msg);
            return res;
        }
    }

    var testEqCallback = function () {
        /*
            sort of emulate how deferreds work in Twisted
            for "convenient" testing - returns a function that
            checks the Deferred callback argument supplied.
        */
        var args = [];
        for (var i = 0; i < arguments.length; i++) {
            args.push(arguments[i]);
        }
        return function (res) {
            var nargs = args.slice();
            nargs.unshift(res);
            is.apply(this, nargs);
            return res;
        }
    }

    var neverHappen = function (d) {
        ok(false, "this should never happen");
    }


    // Test MochiKit async functions - Deferred, etc
    // These are verbatim copies of the Mochikit async tests, and are
    // intended in part to confirm that the test runner framework is OK.

    /*
        Test normal Deferred operation
    */
    var d = new Deferred();
    d.addCallback(testEqCallback(1, "pre-deferred callback"));
    d.callback(1);
    d.addCallback(increment);
    d.addCallback(testEqCallback(2, "post-deferred callback"));
    d.addCallback(throwStuff);
    d.addCallback(neverHappen);
    d.addErrback(catchStuff);
    d.addCallback(testEqCallback(2, "throw -> err, catch -> success"));
    d.addCallback(returnError);
    d.addCallback(neverHappen);
    d.addErrback(catchStuff);
    d.addCallback(testEqCallback(2, "return -> err, catch -> succcess"));

    /*
        Test Deferred cancellation
    */
    var cancelled = function (d) {
        ok(true, "canceller called!");
    }

    var cancelledError = function (res) {
        ok(res instanceof CancelledError, "CancelledError here");
    }

    d = new Deferred(cancelled);
    d.addCallback(neverHappen);
    d.addErrback(cancelledError);
    d.cancel();

    /*
        Test succeed / fail
    */

    d = succeed(1).addCallback(testEqCallback(1, "succeed"));

    // default error
    d = fail().addCallback(neverHappen);
    d = d.addErrback(anythingOkCallback("default fail"));

    // default wrapped error
    d = fail("web taco").addCallback(neverHappen).addErrback(catchStuff);
    d = d.addCallback(testEqCallback("web taco", "wrapped fail"));

    // default unwrapped error
    d = fail(new GenericError("ugh")).addCallback(neverHappen).addErrback(catchStuff);
    d = d.addCallback(testEqCallback("ugh", "unwrapped fail"));

    /*
        Test deferred dependencies
    */

    var deferredIncrement = function (res) {
        var rval = succeed(res);
        rval.addCallback(increment);
        return rval;
    }

    d = succeed(1).addCallback(deferredIncrement);
    d = d.addCallback(testEqCallback(2, "dependent deferred succeed"));

    var deferredFailure = function (res) {
        return fail(res);
    }

    d = succeed("ugh").addCallback(deferredFailure).addErrback(catchStuff);
    d = d.addCallback(testEqCallback("ugh", "dependent deferred fail"));

    /*
        Test double-calling, double-failing, etc.
    */
    try {
        succeed(1).callback(2);
        neverHappen();
    } catch (e) {
        ok(e instanceof AlreadyCalledError, "double-call");
    }
    try {
        fail(1).errback(2);
        neverHappen();
    } catch (e) {
        ok(e instanceof AlreadyCalledError, "double-fail");
    }
    try {
        d = succeed(1);
        d.cancel();
        d = d.callback(2);
        ok(true, "swallowed one callback, no canceller");
        d.callback(3);
        neverHappen();
    } catch (e) {
        ok(e instanceof AlreadyCalledError, "swallow cancel");
    }
    try {
        d = new Deferred(cancelled);
        d.cancel();
        d = d.callback(1);
        neverHappen();
    } catch (e) {
        ok(e instanceof AlreadyCalledError, "non-swallowed cancel");
    }

    /* Test incorrect Deferred usage */

    d = new Deferred();
    try {
        d.callback(new Deferred());
        neverHappen();
    } catch (e) {
        ok (e instanceof Error, "deferred not allowed for callback");
    }
    d = new Deferred();
    try {
        d.errback(new Deferred());
        neverHappen();
    } catch (e) {
        ok (e instanceof Error, "deferred not allowed for errback");
    }

    d = new Deferred();
    (new Deferred()).addCallback(function () { return d; }).callback(1);
    try {
        d.addCallback(function () {});
        neverHappen();
    } catch (e) {
        ok (e instanceof Error, "chained deferred not allowed to be re-used");
    }

    /*
        evalJSONRequest test
    */
    var fakeReq = {"responseText":'[1,2,3,4,"asdf",{"a":["b", "c"]}]'};
    var obj = [1,2,3,4,"asdf",{"a":["b", "c"]}];
    isDeeply(obj, evalJSONRequest(fakeReq), "evalJSONRequest");

    try {
        MochiKit.Async.getXMLHttpRequest();
        ok(true, "getXMLHttpRequest");
    } catch (e) {
        ok(false, "no love from getXMLHttpRequest");
    }

    var lock = new DeferredLock();
    var lst = [];
    var pushNumber = function (x) {
        return function (res) { lst.push(x); }
    };
    lock.acquire().addCallback(pushNumber(1));
    is( compare(lst, [1]), 0, "lock acquired" );
    lock.acquire().addCallback(pushNumber(2));
    is( compare(lst, [1]), 0, "lock waiting for release" );
    lock.acquire().addCallback(pushNumber(3));
    is( compare(lst, [1]), 0, "lock waiting for release" );
    lock.release();
    is( compare(lst, [1, 2]), 0, "lock passed on" );
    lock.release();
    is( compare(lst, [1, 2, 3]), 0, "lock passed on" );
    lock.release();
    try {
        lock.release();
        ok( false, "over-release didn't raise" );
    } catch (e) {
        ok( true, "over-release raised" );
    }
    lock.acquire().addCallback(pushNumber(1));
    is( compare(lst, [1, 2, 3, 1]), 0, "lock acquired" );
    lock.release();
    is( compare(lst, [1, 2, 3, 1]), 0, "lock released" );

    var d = new Deferred();
    lst = [];
    d.addCallback(operator.add, 2);
    d.addBoth(operator.add, 4);
    d.addCallback(bind(lst.push, lst));
    d.callback(1);
    is( lst[0], 7, "auto-partial addCallback addBoth" );
    d.addCallback(function () { throw new Error(); });
    ebTest = function(a, b) {
        map(bind(lst.push, lst), arguments);
    };
    d.addErrback(ebTest, "foo");
    is( lst[1], "foo", "auto-partial errback" );
    is( lst.length, 3, "auto-partial errback" );

    /*
        Test DeferredList
    */

    var callList = new Array(3);
    callList[0] = new Deferred();
    callList[1] = new Deferred();
    callList[2] = new Deferred();
    callList[0].addCallback(increment);
    callList[1].addCallback(increment);
    callList[2].addCallback(increment);
    var defList = new DeferredList(callList);
    callList[0].callback(3);
    callList[1].callback(5);
    callList[2].callback(4);

    defList.addCallback(function (lst) {
        is( arrayEqual(lst, [[true, 4], [true, 6], [true, 5]]), 1,
           "deferredlist result ok" );
    });

    /*
        Test fireOnOneCallback
    */

    var callList2 = new Array(3);
    callList2[0] = new Deferred();
    callList2[1] = new Deferred();
    callList2[2] = new Deferred();
    callList2[0].addCallback(increment);
    callList2[1].addCallback(increment);
    callList2[2].addCallback(increment);
    var defList2 = new DeferredList(callList2, true);
    callList2[1].callback(5);
    callList2[0].callback(3);
    callList2[2].callback(4);

    defList2.addCallback(function (lst) {
        is( arrayEqual(lst, [1, 6]), 1, "deferredlist fireOnOneCallback ok" );
    });

    /*
        Test fireOnOneErrback
    */

    var callList3 = new Array(3);
    callList3[0] = new Deferred();
    callList3[1] = new Deferred();
    callList3[2] = new Deferred();
    callList3[0].addCallback(increment);
    callList3[1].addCallback(throwStuff);
    callList3[2].addCallback(increment);
    var defList3 = new DeferredList(callList3, false, true);
    callList3[0].callback(3);
    callList3[1].callback("foo");
    callList3[2].callback(4);

    defList3.addErrback(function (err) {
        is( err.message, "foo", "deferredlist fireOnOneErrback ok" );
    });

    /*
        Test consumeErrors
    */

    var callList4 = new Array(3);
    callList4[0] = new Deferred();
    callList4[1] = new Deferred();
    callList4[2] = new Deferred();
    callList4[0].addCallback(increment);
    callList4[1].addCallback(throwStuff);
    callList4[2].addCallback(increment);
    var defList4 = new DeferredList(callList4, false, false, true);
    defList4.addErrback(neverHappen);
    callList4[1].addCallback(function (arg) {
        is(arg, null, "deferredlist consumeErrors ok" );
    });
    callList4[0].callback(3);
    callList4[1].callback("foo");
    callList4[2].callback(4);

    /*
        Test gatherResults
    */

    var callList5 = new Array(3);
    callList5[0] = new Deferred();
    callList5[1] = new Deferred();
    callList5[2] = new Deferred();
    callList5[0].addCallback(increment);
    callList5[1].addCallback(increment);
    callList5[2].addCallback(increment);
    var gatherRet = gatherResults(callList5);
    callList5[0].callback(3);
    callList5[1].callback(5);
    callList5[2].callback(4);

    gatherRet.addCallback(function (lst) {
        is( arrayEqual(lst, [4, 6, 5]), 1,
           "gatherResults result ok" );
    });

    /*
        Test maybeDeferred
    */

    var maybeDef = maybeDeferred(increment, 4);
    maybeDef.addCallback(testEqCallback(5, "maybeDeferred sync ok"));

    var maybeDef2 = deferredIncrement(8);
    maybeDef2.addCallback(testEqCallback(9, "maybeDeferred async ok"));

    ok( true, "MochiKit synchronous tests finished!");

    // ----------------------------------

    cb_report = function(p,n,nam,msg) {
        var tn = p+n;
        if (!msg) msg = "";
        ok(!msg,"Test complete: "+tn.toString()+": "+nam+": "+msg);
    };

    cb_complete = function(p,n,res) {
        ok(true,"All tests complete: "+n.toString());
        SimpleTest.finish();
    };


    // ----------------------------------

    // Asynchronous test wrappers

    // Wrap synchronous tests
    var t1 = new AsyncTestWrapper(new SimpleTestCases());

    // ----------------------------------

    // Construct suite with asyncronous tests

    // Create test suite object
    function AsyncTestCases() {
    }

    AsyncTestCases.exposeTestFunctionNames = function () {
        return [ "testDelay1", "testDelay1cb"
               , "testDelay2", "testDelay2cb"
               ];
    }

    // Setup and teardown

    AsyncTestCases.prototype.setUp = function() {
    }

    AsyncTestCases.prototype.tearDown = function() {
    }

    checkCallLater = function (originalTime) {
        is(originalTime, this.origtime, "checkCallLater: argument passed in OK");
        is(1, arguments.length, "checkCallLater: argument count right");
        this.count++;
    };

    AsyncTestCases.prototype.testDelay1 = function() {
        this.origtime = (new Date().getTime());
        this.count    = 0;
        return callLater(0.25, bind(checkCallLater, this), this.origtime);
    }

    AsyncTestCases.prototype.testDelay1cb = function() {
        is(this.count, 1, "callback invoked once");
        return null;
    }

    AsyncTestCases.prototype.testDelay2 = function() {
        return wait(0.25, this.origtime).addCallback(bind(checkCallLater, this));
    }

    AsyncTestCases.prototype.testDelay2cb = function() {
        is(this.count, 2, "callback invoked twice");
        return null;
    }

    // wrap this as an async test suite

    var t2 = new AsyncTestWrapper(new AsyncTestCases());

    // Collect the suites into a single test case

    var s = new AsyncSuiteWrapper();
    //s.addTestSuite(t1);
    //s.addTestSuite(t2);
    SimpleTest.waitForExplicitFinish();
    //s.initiateTests(0,cb_report,cb_complete);
    t2.initiateTests(0,cb_report,cb_complete);

} catch (err) {

    SimpleTest.finish();
    var s = "test suite failure!\n";
    var o = {};
    var k = null;
    for (k in err) {
        // ensure unique keys?!
        if (!o[k]) {
            s +=  k + ": " + err[k] + "\n";
            o[k] = err[k];
        }
    }
    ok ( false, s );

}
</script>
</pre>
</body>
</html>
